﻿using KSJApi3D.NET;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Xml.Linq;
using System.IO;
using System.Security.Cryptography;
using System.Runtime.InteropServices;
using System.Drawing;
using System.Drawing.Imaging;
using static System.Net.Mime.MediaTypeNames;
using System.Security.Policy;

namespace ThicknessLibrary
{
    /// <summary>
    /// 设备信息
    /// </summary>
    public struct DEVICEINFO
    {
        public int nIndex;
        public int DeviceType;
        public int nSerials;
        public ushort wFirmwareVersion;
        public ushort wFpgaVersion;
        public bool Is3DCamera;
    };
    /// <summary>
    /// 采集类型枚举
    /// </summary>
    public enum CAPTURE_TYPE
    {
        PROFILE,
        UNIFORMX_PROFILE,
        ZMAP,
        ZMAP_IDENSITY,
        POINTCLOUD,
        POINTCLOUD_IDENSITY
    };
    public enum UNIFORM_TYPE
    {
        None,
        X,
        Y
    }

    public class KSJCamera
    {
        #region 成员
        public UC3D CurrentCamera;
        public DEVICEINFO DeviceInfo = new DEVICEINFO();
        public int CameraIndex = 0; //相机索引
        public string ConfigFile { set; get; }
        #endregion

        #region 属性
        KSJ_LIVE_IMAGE_CALLBACK m_LiveCallback;//回调函数
        public Action<int> LiveImageCallback;
        IntPtr m_lpContext;//回调的上下文句柄
        KSJ_PROFILE_DATA_CALLBACKEX m_ProfileCallback;

        CAPTURE_TYPE m_nDataType;//测试类型
        UNIFORM_TYPE m_nUniform;
        public int NumberOfProfiles;
        public int StartTriggerFilter;
        public int StartTriggerDelay;
        public int ColStart;
        public int RowStart;
        public int ColSize;
        public int RowSize;
        public float ExpTimeMS;
        public int Gain;
        public int Threshold;
        public int ThresholdLow;
        public int ThresholdWidth;
        public float ZMin;
        public float ZMax;
        public float FovNear;
        public float FovFar;
        public float Profiley;
        public int EncoderDirection;
        public uint Reversal;
        public int DataTriggerDivider;
        public int DataTriggerDelay;
        public int DataTriggerFilter;
        public int InternalFrequencyHz;
        public KSJ3D_DATA_TRIGGER_MODE TriggerMode;
        public byte[] m_PreviewData;//显示预览的数组
        int m_nHeight;
        int MaxRowSize, MaxColSize;

        public Bitmap PreviewBitmap;//预览位图
        public Bitmap ProfileBitmap;

        public int m_nPointNum;

        bool m_bZMap;//绘图用，为true表示ZMap图为彩图，false为黑白图
        bool g_init = false;

        public float[] X_ProfileData;//轮廓x数组
        public float[] Z_ProfileData;//轮廓y数组
        public byte[] profiledata;//显示轮廓的数组

        public bool m_one { set; get; } = false;

        public bool IsConnect { set; get; } = false;

        private bool IsCollectDown = false;
        #endregion

        #region 函数
        public KSJCamera(string configFile = null)
        {
            ConfigFile = configFile;
        }
        /// <summary>
        /// 获取设备列表
        /// </summary>
        /// <returns></returns>
        public static List<string> GetDeviceList()
        {
            int deviceCount = 0;
            List<string> devices = new List<string>();
            UC3D temp = new UC3D();
            temp.KSJ3D_UnInitial();
            temp.KSJ3D_Inital();
            temp.KSJ3D_GetDeviceCount(ref deviceCount);
            for (int i = 0; i < deviceCount; i++)
            {
                bool bIs3DCamera = false;
                string szCameraName = "";

                DEVICEINFO dEVICEINFO = new DEVICEINFO();

                temp.KSJ3D_GetDeviceInformation(i, ref dEVICEINFO.DeviceType, ref dEVICEINFO.nSerials, ref dEVICEINFO.wFirmwareVersion, ref dEVICEINFO.wFpgaVersion);
                byte btMajVersion = (byte)((dEVICEINFO.wFirmwareVersion & 0xFF00) >> 8);		// 得到主版本号
                byte btMinVersion = (byte)(dEVICEINFO.wFirmwareVersion & 0x00FF);				// 得到副版本号

                byte btFpgaMajVersion = (byte)((dEVICEINFO.wFpgaVersion & 0xFF00) >> 8);		// 得到主版本号
                byte btFpgaMinVersion = (byte)(dEVICEINFO.wFpgaVersion & 0x00FF);				// 得到副版本号

                temp.KSJ3D_Is3DCamera(i, ref bIs3DCamera);
                temp.KSJ3D_GetCameraName(i, ref szCameraName);
                devices.Add($"{szCameraName}_{dEVICEINFO.nSerials}");
            }
            return devices;
        }
        /// <summary>
        /// 连接相机
        /// </summary>
        /// <returns></returns>
        public bool ConnectCamera(string name)
        {
            CurrentCamera = new UC3D();
            CurrentCamera.KSJ3D_UnInitial();
            CurrentCamera.KSJ3D_Inital();
            var cams = KSJCamera.GetDeviceList();
            var CameraIndex = cams.IndexOf(name);
            int IsSuccess = CurrentCamera.KSJ3D_DeviceOpen(CameraIndex);
            if (IsSuccess == 1) //连接成功
            {
                //获取版本号信息
                CurrentCamera.KSJ3D_GetRoiMax(CameraIndex, ref MaxRowSize, ref MaxColSize);
                CurrentCamera.KSJ3D_GetDeviceInformation(CameraIndex,
                    ref DeviceInfo.DeviceType,
                    ref DeviceInfo.nSerials,
                    ref DeviceInfo.wFirmwareVersion,
                    ref DeviceInfo.wFpgaVersion);
                LoadConfig(ConfigFile);
                GetParam();
                SetLaserMode(KSJ_LASER_MODE.LM_NORMAL_OPEN);

                

                m_lpContext = new IntPtr(2121);
                m_LiveCallback = new KSJ_LIVE_IMAGE_CALLBACK(LiveCallback);//声明回调函数
                m_ProfileCallback = new KSJ_PROFILE_DATA_CALLBACKEX(ProfileCallback);

                IsConnect = true;
            }
            return false;
        }
        /// <summary>
        /// 关闭相机
        /// </summary>
        /// <returns></returns>
        public bool CloseCamera()
        {
            SetLaserMode(KSJ_LASER_MODE.LM_NORMAL_CLOSE);
            CurrentCamera.KSJ3D_UnInitial();//释放3d传感器资源
            IsConnect = false;
            return false;
        }
        /// <summary>
        /// 设置采集类型
        /// </summary>
        public void SetCaptureType(CAPTURE_TYPE type)
        {
            m_nDataType = type;
        }
        /// <summary>
        /// 设置曝光
        /// </summary>
        /// <param name="value"></param>
        public bool SetExposureTime(float value)
        {
            var result = CurrentCamera?.KSJ3D_SetExposureTime(CameraIndex, value);
            return (result == 0 ? true : false);
        }
        /// <summary>
        /// 设置增益
        /// </summary>
        /// <param name="value"></param>
        public bool SetGain(int value)
        {
           var result = CurrentCamera?.KSJ3D_SetGain(CameraIndex, value);
           return (result == 0 ? true : false);
        }
        /// <summary>
        /// 设置数据触发模式
        /// </summary>
        public bool SetDataTriggerMode(KSJ3D_DATA_TRIGGER_MODE mode)
        {
            int temp = 0;
            switch (mode)
            {
                case KSJ3D_DATA_TRIGGER_MODE.DTM_FREE_RUN: { temp = 0; }break;
                case KSJ3D_DATA_TRIGGER_MODE.DTM_INTERNAL: { temp = 1; }break;
                case KSJ3D_DATA_TRIGGER_MODE.DTM_EXTERNAL: { temp = 2; }break;
                case KSJ3D_DATA_TRIGGER_MODE.DTM_ENCODER: { temp = 3; }break;
            }
            var result = CurrentCamera?.KSJ3D_SetDataTriggerMode(CameraIndex, temp);
            return (result == 0 ? true : false);
        }
        /// <summary>
        /// 设置激光模式
        /// </summary>
        /// <returns></returns>
        public bool SetLaserMode(KSJ_LASER_MODE mode)
        {
            int temp = 0;
            switch(mode)
            {
                case KSJ_LASER_MODE.LM_NORMAL_OPEN: { temp = 0; }break;  
                case KSJ_LASER_MODE.LM_NORMAL_CLOSE: {  temp = 1; }break;
                case KSJ_LASER_MODE.LM_FLASH: { temp = 2; }break;
            }
            var result = CurrentCamera?.KSJ3D_LaserModeSet(CameraIndex, temp);
            return (result == 0 ? true : false);
        }
        /// <summary>
        /// 设置阈值
        /// </summary>
        /// <param name="value"></param>
        /// <returns></returns>
        public bool SetThreshold(int value)
        {
            var result = CurrentCamera?.KSJ3D_LaserModeSet(CameraIndex, value);
            return (result == 0 ? true : false);
        }
        /// <summary>
        /// 读取配置
        /// </summary>
        /// <param name="filePath"></param>
        public void LoadConfig(string filePath)
        {
            IniFiles load = new IniFiles(filePath);
            if (load.ExistINIFile())        //有配置文件
            {
                string temp;
                CurrentCamera.KSJ3D_LoadParamFromFile(CameraIndex, filePath);
                temp = load.IniReadValue("3D", "TYPE");
                m_nDataType = (CAPTURE_TYPE)int.Parse(temp);
                temp = load.IniReadValue("3D", "UNIFORM");
                m_nUniform = (UNIFORM_TYPE)int.Parse(temp);
            }
        }
        /// <summary>
        /// 保存配置
        /// </summary>
        /// <param name="filePath"></param>
        public void SaveConfig(string filePath)
        {
            CurrentCamera.KSJ3D_SaveParamToFile(CameraIndex, filePath);
            IniFiles save = new IniFiles(filePath);//初始化配置文件
            string temp = ((int)m_nDataType).ToString();
            save.IniWriteValue("3D", "TYPE", temp);
            temp = ((int)m_nUniform).ToString();
            save.IniWriteValue("3D", "UNIFORM", temp);
        }
        /// <summary>
        /// 获取参数
        /// </summary>
        public void GetParam()
        {
            CurrentCamera.KSJ3D_GetMaxNumberOfProfilesToCapture(CameraIndex, ref NumberOfProfiles);
            int nValue = 0, nValue2 = 0;
            bool bValue = true;
            int source = 0;
            int StartTriggerCondition = 0;
            CurrentCamera.KSJ3D_GetStartTrigger(CameraIndex, ref source, ref bValue, ref StartTriggerCondition);
            if (bValue) StartTriggerCondition = (int)StartTriggerCondition + 1;
            else StartTriggerCondition = 0;
            CurrentCamera.KSJ3D_GetStartTriggerParameters(CameraIndex, ref StartTriggerFilter, ref StartTriggerDelay);

            CurrentCamera.KSJ3D_GetRoi(CameraIndex, ref ColStart, ref RowStart, ref ColSize, ref RowSize);
            CurrentCamera.KSJ3D_GetExposureTime(CameraIndex, ref ExpTimeMS);
            CurrentCamera.KSJ3D_GetGain(CameraIndex, ref Gain);
            CurrentCamera.KSJ3D_Get3DLaserLineBrightnessThreshold(CameraIndex, ref Threshold);
            CurrentCamera.KSJ3D_Get3DLaserLineBrightnessLowThreshold(CameraIndex, ref ThresholdLow);
            CurrentCamera.KSJ3D_Get3DLaserLineWidth(CameraIndex, ref ThresholdWidth);
            CurrentCamera.KSJ3D_GetMeasurementRange(CameraIndex, ref ZMin, ref ZMax, ref FovNear, ref FovFar);
            CurrentCamera.KSJ3D_GetYResolution(CameraIndex, ref Profiley);
            CurrentCamera.KSJ3D_GetDataTriggerEncoderParameters(CameraIndex, ref nValue, ref nValue2, ref EncoderDirection, ref Reversal);

            int pDataTriggerMode = 0;
            CurrentCamera.KSJ3D_GetDataTriggerMode(CameraIndex, ref pDataTriggerMode);
            TriggerMode = (KSJ3D_DATA_TRIGGER_MODE)pDataTriggerMode;
            int DataTriggerCondition = 0;
            CurrentCamera.KSJ3D_GetDataTriggerExternalTriggerParameters(CameraIndex, ref DataTriggerDivider, ref DataTriggerDelay, ref DataTriggerFilter, ref DataTriggerCondition);
            //DataTriggerCondition = DataTriggerCondition;
            CurrentCamera.KSJ3D_GetDataTriggerInternalFrequency(CameraIndex, ref InternalFrequencyHz);
        }
        /// <summary>
        /// 采集图像
        /// </summary>
        public void CaptureImage()
        {
            switch(m_nDataType)
            {
                case CAPTURE_TYPE.PROFILE:
                    {
                        CurrentCamera.KSJ3D_SetDataFormat(CameraIndex, (int)KSJ3D_DATA_FORMAT.KSJ3D_DF_NON_UNIFORM);//设置为原始数据模式
                        CurrentCamera.KSJ3D_RegisterLiveImageCB(CameraIndex, m_LiveCallback, m_lpContext);//预览回调
                        CurrentCamera.KSJ3D_RegisterProfileDataCBEx(CameraIndex, m_ProfileCallback, m_lpContext);//轮廓回调
                        float fZSize = ZMax - ZMin;//取横向距离
                        m_nHeight = (int)(fZSize / (FovFar / MaxColSize));//根据比例，计算高度
                        m_PreviewData = new byte[ColSize * RowSize];//预览
                    }
                    break;
            }
            CurrentCamera.KSJ3D_StartAcquisition(CameraIndex);
            while (IsCollectDown == false) ;
            CurrentCamera.KSJ3D_StopAcquisition(CameraIndex);
            IsCollectDown = false ;
        }
        public void StopCaptureImage()
        {
            CurrentCamera.KSJ3D_StopAcquisition(CameraIndex);
        }
        unsafe public void LiveCallback(int nIndex, IntPtr pData, int nWidth, int nHeight, int nBitCount, IntPtr m_lpContext)//预览回调函数
        {
            //相机采集到了一张原始图像会调用此回调
            //nIndex    相机索引，表示哪一台相机
            //pData     指向当前图像数据的内存指针。pData[0]为图像第一行第一个像素灰度值，pData[1]第一行第二个像素灰度值；pData[nWidth]为图像第二行第一个像素灰度值，依此类推
            //nWidth    图像数据的宽度，单位：像素。
            //nHeight   图像数据的高度，单位：像素。
            //nBitCount 图像数据的位图深度，单位：位。原始图像均为8bit灰度图，所以这个值通常是8
            //lpContext 用户上下文指针，这个指针是用户调用KSJ3D_RegisterLiveImageCB时传入的上下文指针
            Marshal.Copy(pData, m_PreviewData, 0, nWidth * nHeight);
            m_bZMap = false;
            PreviewBitmap = BytesToBitmap(m_PreviewData, nWidth, nHeight, nBitCount);//转换位图
            //if (LiveImageCallback != null)
            //    LiveImageCallback(1);
            if (!g_init)//未进行过宽高比例计算
            {
                //scale(pictureBox_preview, nWidth, nHeight, 0);
                g_init = true;//进行设置后标记
            }
            IsCollectDown = true;
        }
        public void ProfileCallback(int nIndex, IntPtr x, IntPtr z, IntPtr pParam, IntPtr lpContext)//轮廓回调
        {
            //相机计算得到一个原始轮廓会调用该回调
            //x指向当前轮廓数据的x方向内存指针，单位：毫米。x[0]为轮廓第一个点的x坐标，x[1]轮廓第二个点的x坐标,依此类推。
            //z指向当前轮廓数据的z方向内存指针，单位：毫米。z[0]为轮廓第一个点的z坐标，z[1]轮廓第二个点的z坐标,依此类推。
            //pParam[指向当前轮廓数据参数结构的内存指针，参数定义请参考PROFILE_DATA_PARAM
            //注意：x 、z为-1000的点代表无效点，说明此位置没有采集到点云数据。一个原始轮廓上有ROI设置的列宽个点，不要越界访问
            PROFILE_DATA_PARAM param = (PROFILE_DATA_PARAM)Marshal.PtrToStructure(pParam, typeof(PROFILE_DATA_PARAM));
            X_ProfileData = new float[param.nPointNum];
            Z_ProfileData = new float[param.nPointNum];
            profiledata = new byte[MaxColSize * m_nHeight];
            Marshal.Copy(x, X_ProfileData, 0, param.nPointNum);
            Marshal.Copy(z, Z_ProfileData, 0, param.nPointNum);

            for (int i = 0; i < param.nPointNum; i++)//拟合轮廓图
            {
                //if (m_px[i] > -1000 && m_pz[i] > -1000)
                //{
                //    ix = (int)((m_px[i] / fRangX) * m_nMaxColSize);
                //    iz = (int)((m_pz[i] / fRangZ) * m_nHeight);
                //    index = ix + iz * m_nMaxColSize;
                //    if (index >= 0 && index < m_nMaxColSize * m_nHeight) 
                profiledata[i] = 0;
                //}
            }

            m_nPointNum = param.nPointNum;
            m_bZMap = false;
            ProfileBitmap = BytesToBitmap(profiledata, MaxColSize, m_nHeight, 8);//转换位图
            //if (!g_initprofile)//未进行过宽高比例计算
            //{
            //    scale(pictureBox_profile, MaxColSize, m_nHeight, 2);
            //    g_initprofile = true;
            //}

//            if (m_save)//保存
//            {
//                string t1, filename;
//                t1 = DateTime.Now.ToString("yyyy-MM-dd");
//                float[] y = new float[param.nPointNum];
//                for (int i = 0; i < param.nPointNum; i++) y[i] = 0;
//                IntPtr py = Marshal.AllocHGlobal(param.nPointNum * 4);
//                py = ArrayToIntptr(y);
//                //System.Diagnostics.Debug.WriteLine("开始保存");
//#if halcon12//使用halcon保存om3文件
//                filename = Application.StartupPath + "\\" + t1 + "-" + DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second.ToString() + DateTime.Now.Millisecond.ToString() + ".om3";
//                HObject Hobjx = null, Hobjy = null, Hobjz = null;
//                HOperatorSet.GenImage1(out Hobjx, "real", m_nColSize, 1, x);
//                HOperatorSet.GenImage1(out Hobjy, "real", m_nColSize, 1, py);
//                HOperatorSet.GenImage1(out Hobjz, "real", m_nColSize, 1, z);
//                HTuple hv_ObjectModel3D;
//                HOperatorSet.XyzToObjectModel3d(Hobjx, Hobjy, Hobjz, out hv_ObjectModel3D);
//                HOperatorSet.WriteObjectModel3d(hv_ObjectModel3D, "om3", filename, "invert_normals", "false");
//                HOperatorSet.ClearObjectModel3d(hv_ObjectModel3D);
//                hv_ObjectModel3D.UnPinTuple();
//                System.Diagnostics.Debug.WriteLine("保存完毕");
               
//#else//不使用halcon保存pcd文件

//                filename = Application.StartupPath + "\\" + t1 + "-" + DateTime.Now.Hour.ToString() + DateTime.Now.Minute.ToString() + DateTime.Now.Second.ToString() + DateTime.Now.Millisecond.ToString() + ".pcd";
//                m_device.KSJ3D_HelperSaveToPCD(m_nColSize, 1, x, py, z, filename);
//                //System.Diagnostics.Debug.WriteLine("保存完毕");
//#endif
//            }

            if (m_one)//如果是一次采集则停止采集
            {
                m_one = false;
                //checkBox_one.BeginInvoke((MethodInvoker)(() => { checkBox_one.Checked = false; }));
            }
        }
        public unsafe System.Drawing.Bitmap BytesToBitmap(byte[] bytes, int width, int height, int nBitCount)//将数组转换为位图
        {
            try
            {
                PixelFormat bitmaptype = PixelFormat.Format8bppIndexed;
                int nPixelBytes = nBitCount >> 3;
                int ColSize;
                if (width % 4 != 0) ColSize = ((width * 8 + 31) / 32 * 4);//4对齐
                else ColSize = width;
                if (3 == nPixelBytes)
                {
                    bitmaptype = PixelFormat.Format24bppRgb;
                }
                else if (4 == nPixelBytes)
                {
                    bitmaptype = PixelFormat.Format32bppPArgb;
                }

                Bitmap bitmap = new Bitmap(ColSize, height, bitmaptype);
                //获取图像的BitmapData对像 
                BitmapData bmpData = bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmaptype);

                byte* ptr = (byte*)(bmpData.Scan0);
                int nWidthBytes = width * nPixelBytes;
                int nColBytes = ColSize * nPixelBytes;
                int nSrcOffset = 0;
                int nDesOffset = 0;
                for (int i = 0; i < height; i++)
                {
                    nSrcOffset = (height - i - 1) * nWidthBytes;
                    nDesOffset = 0;
                    for (int j = 0; j < width; j++)
                    {
                        for (int k = 0; k < nPixelBytes; k++)
                        {
                            *(ptr + nDesOffset + k) = bytes[nSrcOffset + k];
                        }

                        nDesOffset += nPixelBytes;
                        nSrcOffset += nPixelBytes;
                    }

                    ptr = ptr + nColBytes;
                }

                bitmap.UnlockBits(bmpData);  // 解锁内存区域

                if (1 == nPixelBytes)
                {
                    // 修改生成位图的索引表，从伪彩修改为灰度
                    ColorPalette palette;
                    // 获取一个Format8bppIndexed格式图像的Palette对象
                    using (Bitmap bmp = new Bitmap(1, 1, PixelFormat.Format8bppIndexed))
                    {
                        palette = bmp.Palette;
                    }

                    if (File.Exists("color.dat") && m_bZMap)
                    {
                        FileStream fs = new FileStream("color.dat", FileMode.Open);
                        BinaryReader br = new BinaryReader(fs);
                        Byte[] byData = br.ReadBytes((int)fs.Length);
                        fs.Close();
                        palette.Entries[0] = Color.FromArgb(0, 0, 0);
                        for (int i = 1; i < 256; i++)
                        {
                            palette.Entries[i] = Color.FromArgb(byData[i * 4 + 2], byData[i * 4 + 1], byData[i * 4]);
                        }
                    }
                    else
                        for (int i = 0; i < 256; i++)
                        {
                            palette.Entries[i] = Color.FromArgb(i, i, i);
                        }
                    // 修改生成位图的索引表
                    bitmap.Palette = palette;
                }

                return bitmap;
            }
            catch (ArgumentNullException ex)
            {
                throw ex;
            }
            catch (ArgumentException ex)
            {
                throw ex;
            }
        }
        #endregion
    }
}
